package com.alameda.service.impl.user;

import com.alameda.common.base.constant.LoginConstant;
import com.alameda.common.base.enums.UserTypeEnum;
import com.alameda.common.commonpent.redis.api.RedisCacheTemplate;
import com.alameda.common.exception.BusinessException;
import com.alameda.common.utils.*;
import com.alameda.facade.user.ApUserFacade;
import com.alameda.mapper.user.ApUserMapper;
import com.alameda.pojo.user.ApUserDO;
import com.alameda.service.user.ApUserService;
import com.alameda.statement.enums.AdSystemRecordEnum;
import com.alameda.user.bo.ApUserBO;
import com.alameda.user.dto.ApUserDTO;
import com.alameda.user.vo.ApUserVO;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

import static com.alameda.common.base.constant.LoginConstant.TOKEN;

/**
 * 移动端用户 业务逻辑层 具体实现
 *
 * @author Lv
 * @date 2024/09/22
 */
@Slf4j
@Service
public class ApUserServiceImpl extends ServiceImpl<ApUserMapper, ApUserDO> implements ApUserService {

    @Autowired
    private ApUserMapper apUserMapper;

    @Autowired
    private RedisCacheTemplate redisCacheTemplate;

    @Autowired
    private ApUserFacade apUserFacade;

    /**
     * 登录移动端
     *
     * @param apUserDTO 移动端用户信息
     * @return
     */
    @Override
    public Map<String, Object> login(ApUserDTO apUserDTO) {
        ApUserDO apUserDO = findByUserName(apUserDTO.getUsername());
        if (PubUtils.isNull(apUserDO)) {
            BusinessException.throwException("账号错误");
        }

        if (!apUserDO.getPassword().equals(MdFiveUtil.getMdFiveString(apUserDTO.getPassword()))) {
            BusinessException.throwException("密码错误");
        }

        // 获取用户类型
        Map<String, String> extDataMap = apUserDO.getExtData();
        String userType = extDataMap.get(UserTypeEnum.getKeyString());

        //登录成功 生成token
        Map<String, Object> claimMap = new HashMap<>(16);
        claimMap.put(LoginConstant.ID, apUserDO.getId());
        claimMap.put(LoginConstant.USERNAME, apUserDO.getUsername());
        claimMap.put(LoginConstant.USER_TYPE, userType);
        String token = JwtUtils.genToken(claimMap);

        // token 写入redis 5个小时过期
        redisCacheTemplate.setCacheObject(token, token, 5, TimeUnit.HOURS);

        Map<String, Object> map = MapUtils.newHashMap();
        map.put(TOKEN, token);

        // 存储系统信息记录
        apUserFacade.saveRecord(AdSystemRecordEnum.LOGIN);
        // token写入db
        saveTokenInfo(apUserDO, token);
        return map;
    }

    /**
     * 将token写入数据表
     *
     * @param apUserDO 移动端用户信息
     * @param token    token值
     * @return
     */
    private Boolean saveTokenInfo(ApUserDO apUserDO, String token) {
        Map<String, String> extDataMap = apUserDO.getExtData();
        extDataMap.put(TOKEN, token);
        apUserDO.setExtData(extDataMap);
        return this.updateById(apUserDO);
    }

    /**
     * 获取当前登录用户信息
     *
     * @return
     */
    @Override
    public ApUserVO getInfo() {
        String userName = ThreadLocalUtils.getUserName();
        ApUserDO adUserDO = findByUserName(userName);

        ApUserVO apUserVO = ObjectUtils.convert(adUserDO, ApUserVO.class);
        apUserVO.setAvatar("https://wpimg.wallstcn.com/f778738c-e4f8-4870-b634-56703b4acafe.gif");
        apUserVO.setRoles(Arrays.asList("admin"));
        apUserVO.setPermissions(Arrays.asList("*:*:*"));
        apUserVO.setName(adUserDO.getUsername());
        return apUserVO;
    }

    /**
     * 退出登录
     *
     * @return
     */
    @Override
    public Boolean logout() {
        String token = ThreadLocalUtils.getToken();
        // 移除db中的token
        removeDbToken(token);
        return redisCacheTemplate.deleteObject(token);
    }

    /**
     * 移除db中的token
     *
     * @param token 登录令牌
     * @return
     */
    private Boolean removeDbToken(String token) {
        ApUserDO apUserDO = findByToken(token);
        Map<String, String> extDataMap = apUserDO.getExtData();
        extDataMap.put(TOKEN, null);
        return this.updateById(apUserDO);
    }

    /**
     * 查询用户列表
     *
     * @param apUserDTO 移动端用户信息
     * @return
     */
    @Override
    public IPage<ApUserVO> listUserInfo(ApUserDTO apUserDTO) {
        IPage<ApUserDO> page = new Page<>(apUserDTO.getPageNum(), apUserDTO.getPageSize());
        LambdaQueryWrapper<ApUserDO> lambdaQueryWrapper = getLambdaQueryWrapper(apUserDTO);
        IPage<ApUserDO> pageData = this.page(page, lambdaQueryWrapper);
        return ObjectUtils.convertPageData(pageData, ApUserVO.class);
    }

    /**
     * 保存用户
     *
     * @param apUserDTO 移动端用户信息
     * @return
     */
    @Override
    public Boolean saveUser(ApUserDTO apUserDTO) {
        // 校验数据
        checkData(apUserDTO);
        // 写入数据
        return saveOrUpdateUser(apUserDTO);
    }

    /**
     * 保存或修改用户信息
     *
     * @param apUserDTO 移动端用户信息
     * @return
     */
    private Boolean saveOrUpdateUser(ApUserDTO apUserDTO) {
        ApUserDO apUserDO = new ApUserDO();
        if (PubUtils.isNotEmpty(apUserDTO.getPassword())) {
            apUserDO.setPassword(MdFiveUtil.getMdFiveString(apUserDTO.getPassword()));
        }
        apUserDO.setId(apUserDTO.getId());
        apUserDO.setUsername(apUserDTO.getUsername());
        apUserDO.setNickname(apUserDTO.getNickname());
        apUserDO.setEmail(apUserDTO.getEmail());
        apUserDO.setUserPic(apUserDTO.getUserPic());
        return this.saveOrUpdate(apUserDO);
    }

    /**
     * 修改用户
     *
     * @param apUserDTO 移动端用户信息
     * @return
     */
    @Override
    public Boolean updateUser(ApUserDTO apUserDTO) {
        return saveOrUpdateUser(apUserDTO);
    }

    /**
     * 导出excel
     *
     * @param apUserDTO 移动端用户信息
     */
    @Override
    public void exportExcel(ApUserDTO apUserDTO) {
        List<ApUserBO> apUserBOList = getExportData(apUserDTO);
        ExcelUtils.export(apUserBOList, ApUserBO.class, "移动端用户");
    }

    /**
     * 处理移动端token续期
     *
     * @param token 登录令牌
     * @return
     */
    @Override
    public Boolean dealApUserTokenRenewal(String token) {
        ApUserDO apUserDO = findByToken(token);
        if (PubUtils.isNull(apUserDO)) {
            // do nothing
        } else {
            log.info("处理用户[{}]登录续期", apUserDO.getUsername());
            // 写入redis续期
            redisCacheTemplate.setCacheObject(token, token, 5, TimeUnit.HOURS);
        }
        return Boolean.TRUE;
    }

    /**
     * 通过登录令牌获取用户信息
     *
     * @param token 登录令牌
     * @return
     */
    private ApUserDO findByToken(String token) {
        LambdaQueryWrapper<ApUserDO> lambdaQueryWrapper = Wrappers.lambdaQuery(ApUserDO.class).apply("ext_data ->> '$.token' = {0}", token);
        return this.getOne(lambdaQueryWrapper);
    }

    /**
     * 获取导出数据
     *
     * @param apUserDTO 用户信息
     * @return
     */
    private List<ApUserBO> getExportData(ApUserDTO apUserDTO) {
        if (PubUtils.isEmpty(apUserDTO.getIdList())) {
            IPage<ApUserVO> pageData = listUserInfo(apUserDTO);
            return ObjectUtils.convertList(pageData.getRecords(), ApUserBO.class);
        } else {
            List<ApUserDO> apUserDOList = this.findByIdInOrderByCreateTimeDesc(apUserDTO.getIdList());
            return ObjectUtils.convertList(apUserDOList, ApUserBO.class);
        }
    }

    /**
     * 最小长度
     */
    private static final Short MIN_LENGTH = 5;

    /**
     * 最大长度
     */
    private static final Short MAX_LENGTH = 16;

    /**
     * 校验数据
     *
     * @param apUserDTO 移动端用户信息
     */
    private void checkData(ApUserDTO apUserDTO) {
        String username = apUserDTO.getUsername();
        if (PubUtils.isEmpty(username) || username.length() < MIN_LENGTH || username.length() > MAX_LENGTH) {
            BusinessException.throwException("注册账号有误");
        }

        ApUserDO apUserDO = findByUserName(username);
        if (PubUtils.isNotNull(apUserDO)) {
            BusinessException.throwException("账号已注册");
        }

        String password = apUserDTO.getPassword();
        if (PubUtils.isEmpty(password) || password.length() < MIN_LENGTH || password.length() > MAX_LENGTH) {
            BusinessException.throwException("注册密码有误");
        }
    }

    /**
     * 获取查询条件构造
     *
     * @param apUserDTO 移动端用户信息
     * @return
     */
    private LambdaQueryWrapper<ApUserDO> getLambdaQueryWrapper(ApUserDTO apUserDTO) {
        return Wrappers.lambdaQuery(ApUserDO.class)
                .like(PubUtils.isNotEmpty(apUserDTO.getUsername()), ApUserDO::getUsername, apUserDTO.getUsername())
                .like(PubUtils.isNotEmpty(apUserDTO.getNickname()), ApUserDO::getNickname, apUserDTO.getNickname())
                .like(PubUtils.isNotEmpty(apUserDTO.getEmail()), ApUserDO::getEmail, apUserDTO.getEmail());
    }
}
